package com.wl.dzt.sys.controller.param;

import com.google.gson.reflect.TypeToken;
import com.wl.dzt.basic.user.User;
import com.wl.dzt.sys.controller.action.Action;
import com.wl.dzt.sys.controller.edit.DateEditor;
import com.wl.dzt.sys.controller.param.shir.ShirBean;
import com.wl.dzt.sys.controller.param.shir.ShirPaser;
import com.wl.dzt.sys.controller.view.View;
import com.wl.dzt.sys.core.dao.curd.validate.Validate;
import com.wl.dzt.sys.core.dao.datachange.DataChangeter;
import com.wl.dzt.sys.core.dao.select.Select;
import com.wl.dzt.sys.core.dao.select.order.Sort;
import com.wl.dzt.sys.core.dao.select.term.Term;
import com.wl.dzt.sys.core.dao.tree.TreeSetting;
import com.wl.dzt.sys.util.*;
import com.wl.dzt.sys.util.clazz.ClassUtil;
import com.wl.dzt.sys.util.json.Json;
import com.wl.dzt.sys.util.json.JsonConvert;
import org.springframework.web.bind.ServletRequestDataBinder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author wl_code@163.com
 * @version 1.0
 * @date 2018/7/5/005
 * @description 请求参数
 * 用于传递动作执行器相关的参数
 */
public class Param {
    private Param() {
    }

    /**
     * 请求类
     */
    private Class<?> clazz;

    /**
     * 动作
     */
    private Action action;

    /**
     * 视图
     */
    private View view;

    /**
     * 操作对象
     */
    private Object entity;

    /**
     * 操作id
     */
    private String id;

    /**
     * 请求字段
     */
    private String[] fields = new String[]{};

    /**
     * 分页参数
     */
    private Integer start = 0, limit = 0;

    /**
     * 查询条件
     */
    private List<Term> terms;

    /**
     * 排序
     */
    private List<Sort> sorts;

    /**
     * 验证
     */
    private List<Validate> validates = new ArrayList<>();

    /**
     * 树形映射表达式
     */
    private TreeSetting treeSetting;

    /**
     * 树形多选模式
     */
    private String treeCheckedModel;

    /**
     * 树形节点开始遍历id
     */
    private String startId;

    /**
     * 转发地址
     */
    private String url;

    /**
     * 批量处理数据
     */
    private List<?> data = new ArrayList();

    /**
     * json转换器
     */
    private JsonConvert<?> convert;

    /**
     * 数据转换器
     */
    private DataChangeter dataChangeter;

    /**
     * 拦截器
     */
    private ShirBean supportHandlerInterceptor;

    /**
     * 数据访问规则
     */
    private List<Term> rules = new ArrayList<>();

    /**
     * 请求
     */
    private HttpServletRequest request;

    /**
     * sql 键
     */
    private String sqlKey;

    /**
     * 节点包含自己
     */
    private Boolean self;

    /**
     * <b>创建参数</b>
     * <p>
     * 根据请求创建动作执行器
     * </p>
     *
     * @param request servlet请求
     * @return web请求参数
     * @throws Exception 异常
     */
    public static Param create(HttpServletRequest request) throws Exception {
        Param param = new Param();
        param.request = request;
        param.action = SpringUtil.getBean(request.getParameter("$_action"));
        param.view = SpringUtil.getBean(request.getParameter("$_view"));
        param.id = request.getParameter("id");
        param.treeCheckedModel = request.getParameter("$_treeCheckedModel");
        // 类和实体
        String clazz = request.getParameter("$_clazz");
        if (!StringUtil.emptyStr(clazz)) {
            param.clazz = ClassUtil.createClass(request.getParameter("$_clazz"));
            param.entity = bind(request, param.clazz);
        }
        // 字段
        String fs = request.getParameter("$_fields");
        param.fields = fs == null ? param.fields : fs.split(",");
        // 分页
        String start = request.getParameter("start"), limit = request.getParameter("limit");
        if (!"0".equals(limit) && limit != null) {
            param.limit = Integer.valueOf(limit);
            param.start = Integer.valueOf(start);
        }
        // 查询条件
        String trs = request.getParameter("$_where");
        param.terms = StringUtil.emptyStr(trs) ? new ArrayList<Term>() : Json.recover(trs, JsonFormat.LIST_TERM_TYPE);
        // 排序
        String sts = request.getParameter("$_order");
        param.sorts = StringUtil.emptyStr(sts) ? new ArrayList<Sort>() : Json.recover(sts, JsonFormat.LIST_SORT_TYPE);
        // 验证
        String validateStr = request.getParameter("$_validate");
        param.validates = StringUtil.emptyStr(validateStr) ? new ArrayList<Validate>() : Json.recover(validateStr,
                JsonFormat.LIST_VALIDATE_TYPE);

        // 树形包含起始节点
        String self = request.getParameter("$_self");
        param.self = StringUtil.emptyStr(self) ? false : new Boolean(self);
        // 树形映射表达式
        String mapper = request.getParameter("$_mapper"), startId = request.getParameter("$_startId");
        if (!StringUtil.emptyStr(mapper)) {
            TreeSetting setting = TreeSetting.createByExp(mapper);
            setting.setStartId(StringUtil.emptyStr(startId) ? "root" : startId);
            setting.setContainOneself(param.self);
            param.treeSetting = setting;
            param.fields = mapper.substring(0, 1).equals("#") ? param.fields : mapper.split(",");
            setting.setFields(param.fields);
            List<String> lst = new ArrayList<String>(Arrays.asList(param.fields));
            lst.remove("*");
            param.fields = lst.toArray(new String[lst.size()]);
        }
        // 转发地址
        String url = request.getParameter("$_url");
        if (!StringUtil.emptyStr(url)) {
            param.url = url;
            param.action = SpringUtil.getBean("urlFlowerAction");
            param.view = SpringUtil.getBean("urlFlowerView");
        }

        // 批量数据处理
        String json = request.getParameter("$_json"), convert = request.getParameter("$_convert");
        if (!LogicUtil.exitNull(json)) {
            // 存在转换器
            if (!LogicUtil.isNull(convert)) {
                param.convert = SpringUtil.getBean(convert);
            } else {
                param.convert = (str) -> {
                    Type type = new TypeToken<List<Map<String, Object>>>() {
                    }.getType();
                    return Json.recover(json, type);
                };
            }
            param.data = param.convert.parser(json);
        }

        // 数据修改器
        String dataChangeterStr = request.getParameter("$_dataChangeter");
        if (!StringUtil.emptyStr(dataChangeterStr)) {
            param.dataChangeter = SpringUtil.getBean(dataChangeterStr);
        }

        // 数据过滤规则
        String rule = request.getParameter("$_rule");
        if (!StringUtil.emptyStr(rule)) {
            param.rules = paserRule(Json.recover(rule, JsonFormat.LIST_TERM_TYPE), request);
        }

        // 过程拦截器
        String shir = request.getParameter("$_shir");
        if (!StringUtil.emptyStr(shir)) {
            param.supportHandlerInterceptor = ShirPaser.paser(shir);
        }

        // sql键
        param.sqlKey = StringUtil.emptyStr(request.getParameter("$_sqlKey")) ? null : request.getParameter("$_sqlKey");

        return param;
    }

    /**
     * <b>绑定实体</b>
     * <p>
     * 根据请求转换实体<br/>
     * 并且执行日期转换
     * </p>
     *
     * @param request
     * @param entityClass
     * @return
     * @throws Exception
     */
    private static Object bind(HttpServletRequest request, Class<?> entityClass) throws Exception {
        if (entityClass == null) {
            return null;
        }
        Object entity = entityClass.newInstance();
        ServletRequestDataBinder binder = new ServletRequestDataBinder(entity);
        // 转换时间
        binder.registerCustomEditor(Date.class, new DateEditor());
        // 绑定实体
        binder.bind(request);
        return entity;
    }

    /**
     * 规则过滤
     */
    private RuleFilter ruleFilter = new RuleFilter();

    /**
     * <b>转换查询对象</b>
     * <p>
     * 把当前参数转换为查询对象
     * </p>
     *
     * @return 查询条件封装
     */
    public Select toSelect() {
        // 查询条件
        List<Term> where = new ArrayList<>();
        where.addAll(this.getTerms());

        // 过滤规则
        ruleFilter.filterRule(this);
        where.addAll(this.getRules());
        Term[] trs = where.toArray(new Term[where.size()]);
        Sort[] sts = this.getSorts().toArray(new Sort[this.getSorts().size()]);
        Select select = Select.create(this.getClazz()).page(this.getStart(), this.getLimit()).fields(this.getFields()).where(trs)
                .order(sts);
        return select;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public Action getAction() {
        return action;
    }

    public View getView() {
        return view;
    }

    public Object getEntity() {
        return entity;
    }

    public String getId() {
        return id;
    }

    public String[] getFields() {
        return fields;
    }

    public Integer getStart() {
        return start;
    }

    public Integer getLimit() {
        return limit;
    }

    public List<Term> getTerms() {
        return terms;
    }

    public List<Sort> getSorts() {
        return sorts;
    }

    public List<Validate> getValidates() {
        return validates;
    }

    public TreeSetting getTreeSetting() {
        return treeSetting;
    }

    public String getUrl() {
        return url;
    }

    public List<?> getData() {
        return data;
    }

    public JsonConvert<?> getConvert() {
        return convert;
    }

    public String getStartId() {
        return startId;
    }

    public DataChangeter getDataChangeter() {
        return dataChangeter;
    }

    public String getTreeCheckedModel() {
        return treeCheckedModel;
    }

    public ShirBean getSupportHandlerInterceptor() {
        return supportHandlerInterceptor;
    }

    // 请求是否成功
    private boolean success = true;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public List<Term> getRules() {
        return rules;
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public String getSqlKey() {
        return sqlKey;
    }

    public void setEntity(Object entity) {
        this.entity = entity;
    }

    /**
     * <b>转换规则函数</b>
     * <p>
     * 将规则表达式转换为查询条件
     * </p>
     *
     * @param list
     * @param request
     * @return
     */
    private static List<Term> paserRule(List<Term> list, HttpServletRequest request) {
        List<Term> relayes = new ArrayList<>();
        User user = WebUtils.getUser(request);
        list.forEach(term -> {
            Term tr = new Term();
            Object key = term.getValue();
            if (key instanceof String[]) {
                key = ((String[]) key)[0];
            }
            Object val = user.getAttr(key.toString());
            tr.setField(term.getField());
            tr.setOp(term.getOp());
            tr.setValue(val);
            relayes.add(tr);
        });
        return relayes;
    }

    public Boolean getSelf() {
        return self;
    }

}
